﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using CC98.Network.Primitives;
using CC98.Security;

namespace CC98.Network
{
    public class CC98HttpClient
    {
        public CC98HttpClient(string username)
            : this(new CC98HttpClientHandler(username))
        {
            // Nothing
        }

        internal CC98HttpClient(CC98HttpClientHandler handler)
        {
            if (handler == null)
                throw new ArgumentNullException("handler");
            this._handler = handler;
            this._handler.AuthentionStateChangedCallback = this.OnAuthenticationStateChanged;
            this._client = new HttpClient(handler);
        }

        /// <summary>
        /// Is authenticated
        /// </summary>
        public bool? IsAuthenticated
        {
            get
            {
                return this._handler.IsAuthenticated;
            }
        }

        /// <summary>
        /// User id
        /// </summary>
        public int UserID
        {
            get
            {
                return this._handler.UserID;
            }
        }

        /// <summary>
        /// Username
        /// </summary>
        public string Username
        {
            get
            {
                return this._handler.Username;
            }
        }

        /// <summary>
        /// Login time
        /// </summary>
        public DateTime? LoginTime
        {
            get
            {
                return this._handler.LoginTime;
            }
        }

        /// <summary>
        /// Login async
        /// </summary>
        /// <param name="certificate"></param>
        /// <returns></returns>
        public IAsyncOperation<LoginResult> LoginAsync(CC98Certificate certificate)
        {
            if (certificate == null)
                throw new ArgumentNullException("certificate");

            return AsyncInfo.Run<LoginResult>(new Func<CancellationToken, Task<LoginResult>>((ct) =>
                {
                    return this.InternalLoginAsync(certificate);
                }));
        }

        /// <summary>
        /// Login async
        /// </summary>
        /// <param name="certificate">CC98 Certificate</param>
        /// <returns></returns>
        internal async Task<LoginResult> InternalLoginAsync(CC98Certificate certificate)
        {
            if (certificate == null)
                throw new ArgumentNullException("certificate");
            if (certificate.IsAnonymous)
                throw new InvalidOperationException("Anonymous certificate cannot login");
            if (certificate.Username != this.Username)
                throw new ArgumentException("Username not match");
            if (this.IsAuthenticated ?? false)
                return new LoginResult(true, null);
            // Login
            HttpRequestMessage reqMsg = new HttpRequestMessage(HttpMethod.Post, LoginUrl);
            // Encoding
            FormUrlEncodedContent content = new FormUrlEncodedContent(new KeyValuePair<string, string>[]
            {
                new KeyValuePair<string,string>("a", "i"),
                new KeyValuePair<string,string>("u", certificate.Username),
                new KeyValuePair<string, string>("p", this.GetEncodedPassword(certificate.Password)),
                new KeyValuePair<string,string>("userhidden","2")
            });
            reqMsg.Content = content;

            var resp = await this._client.SendAsync(reqMsg);
            var respContent = await resp.Content.ReadAsStringAsync();

            if (respContent == "9898")
            {
                return new LoginResult(true, null);
            }
            else
            {
                return new LoginResult(true, new LoginFailedException());
            }
        }

        /// <summary>
        /// Get the session object represents the current client
        /// </summary>
        /// <returns></returns>
        public Session GetSession()
        {
            return this._handler.GetSession();
        }

        /// <summary>
        /// Sine logout don't need any communication with server, so this is not an async method
        /// </summary>
        public void Logout()
        {
            this._handler.ResetHandler();
        }

        public async Task<object> GetHomeAsync()
        {
            return await this._client.GetAsync(HomeUrl);
        }

        public async Task<object> GetBoardAsync(int id, int pageNo = 1)
        {
            return await this._client.GetAsync(string.Format(BoardUrlFormat, id, pageNo));
        }

        public async Task<object> GetThreadAsync(int boardID, int threadID, int pageNo = 1)
        {
            return await this._client.GetAsync(string.Format(ThreadUrlFormat, boardID, threadID, pageNo));
        }

        public event EventHandler<AuthenticationStateChangedEventArgs> AuthenticationStateChanged;

        protected void OnAuthenticationStateChanged(bool? isAuth)
        {
            var eventHandler = this.AuthenticationStateChanged;
            if (eventHandler != null)
                eventHandler(this, new AuthenticationStateChangedEventArgs(isAuth));
        }

        private HttpClient _client;

        private CC98HttpClientHandler _handler;

        private string GetEncodedPassword(string password)
        {
            return password.ComputeMD5();
        }

        #region Url patterns

        /// <summary>
        /// Home url, no parameters
        /// </summary>
        private static readonly string HomeUrl = "http://www.cc98.org/";
        /// <summary>
        /// Board url. Parameter {0} is board id
        /// </summary>
        private static readonly string BoardUrlFormat = "http://www.cc98.org/list.asp?boardid={0}&page={1}";
        /// <summary>
        /// Thread url. Parameter {0} is board id, {1} is thread id, {2} is page number (start from 1)
        /// </summary>
        private static readonly string ThreadUrlFormat = "http://www.cc98.org/dispbbs.asp?boardID={0}&ID={1}&page={2}";
        /// <summary>
        /// Login url
        /// </summary>
        private static readonly string LoginUrl = "http://www.cc98.org/sign.asp";
        /// <summary>
        /// Post thread action url. Parameter {0} is board id, {1} is thread id
        /// </summary>
        private static readonly string PostThreadUrlFormat = "http://www.cc98.org/reannounce.asp?BoardID={0}&id={1}";

        #endregion
    }
}
